;Copyright (C) 2010 Salil Bhagurkar

;This file is part of salil64

;salil64 is free software: you can redistribute it and/or modify
;it under the terms of the GNU Lesser General Public License as published by
;the Free Software Foundation, either version 3 of the License, or
;(at your option) any later version.

;salil64 is distributed in the hope that it will be useful,
;but WITHOUT ANY WARRANTY; without even the implied warranty of
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;GNU Lesser General Public License for more details.

;You should have received a copy of the GNU Lesser General Public License
;along with salil64.  If not, see <http://www.gnu.org/licenses/>.

[BITS 32]
[section .text]
global boot
boot:
jmp boot1


;This is the multiboot header required by grub loader. it specifies the code,data size

ALIGN 4
mboot:

	MULTIBOOT_PAGE_ALIGN	equ 1<<0
	MULTIBOOT_MEMORY_INFO	equ 1<<1
	MULTIBOOT_AOUT_KLUDGE	equ 1<<16
	MULTIBOOT_HEADER_MAGIC	equ 0x1BADB002
	MULTIBOOT_HEADER_FLAGS	equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
	MULTIBOOT_CHECKSUM	equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
	EXTERN _code, _data, _ehframe, _bss, _end


	dd MULTIBOOT_HEADER_MAGIC
	dd MULTIBOOT_HEADER_FLAGS
	dd MULTIBOOT_CHECKSUM


	dd mboot
	dd _code
	dd _bss
	dd _end
	dd boot


;Protected mode GDT
gdt:

gdt_null:
        dd 0
        dd 0

SYS_CODE_SEL	equ	$-gdt
gdt_code:
        dw 0FFFFh
        dw 0
        db 0
        db 0x9A
        db 0xCF
        db 0

SYS_DATA_SEL	equ	$-gdt
gdt_data:
        dw 0FFFFh
        dw 0
        db 0
        db 0x92
        db 0xCF
        db 0

REAL_CODE_SEL	equ	$-gdt
gdt3:	dw 0xFFFF
	dw 0
	db 0
	db 0x9A
	db 0
	db 0

REAL_DATA_SEL	equ	$-gdt
gdt4:	dw 0xFFFF
	dw 0x100
	db 0
	db 0x92
	db 0
	db 0

gdt_end:


gdt_desc:
        dw gdt_end - gdt - 1
        dd gdt


boot1:
	lgdt [gdt_desc]
	jmp SYS_CODE_SEL:flush_ipq
	flush_ipq:
	mov ax,SYS_DATA_SEL
	mov ds,ax
	mov es,ax
	mov fs,ax
	mov gs,ax
	mov ss,ax


	;Detect if long mode is available
	mov eax, 0x80000000    ; Set the A-register to 0x80000000.
	cpuid                  ; CPU identification.
	cmp eax, 0x80000001    ; Compare the A-register with 0x80000001.
	jb .NoLongMode         ; It is less, there is no long mode.
	mov eax, 0x80000001    ; Set the A-register to 0x80000001.
    cpuid                  ; CPU identification.
    test edx, 1 << 29      ; Test if the LM-bit, which is bit 29, is set in the D-register.
    jz .NoLongMode         ; They aren't, there is no long mode.
	jmp LongInit
	.NoLongMode:
	mov esi, longModeError
	mov edi, 0xb8000
	mov ecx, 42
	rep movsb
	jmp $

LongInit:

	mov eax, cr0                                   ; Set the A-reg to control register 0.
	and eax, 01111111111111111111111111111111b     ; Clear the PG-bit, which is bit 31.
	mov cr0, eax                                   ; Set control reg 0 to the A-register.

	;Clear page table area
	mov edi, _pml4
	mov cr3, edi
	xor eax, eax
	mov ecx, 4096
	rep stosd


	;Setup the entries
	;Setup PM4L
	mov edi, _pml4
	mov dword[edi], _pdpt
	or dword[edi], 7

	;Setup PDPT
	mov edi, _pdpt
	mov dword[edi], _pd
	or dword[edi], 7

	;Setup PD
	mov edi, _pd
	mov dword[edi], _pt
	or dword[edi], 7

	;Identity map the first page table
	mov edi, _pt
	mov ecx, 512
	mov ebx, 0x7
	.SetEntry:
	mov dword[edi], ebx
	add ebx, 0x1000
	add edi, 8
	loop .SetEntry



	;Enable PAE
	mov eax, cr4
	or eax, 1 << 5
	mov cr4, eax

	;Set LM(Long Mode) bit in EFER MSR
	mov ecx, 0xC0000080          ; Set the C-register to 0xC0000080, which is the EFER MSR.
	rdmsr                        ; Read from the model-specific register.
	or eax, 1 << 8               ; Set the LM-bit which is the 9th bit.
	wrmsr

	lgdt [GDT64.Pointer]


	mov eax, cr0                 ; Set the A-register to control register 0.
	or eax, 1 << 31              ; Set the PG-bit, which is the 31st bit.
	mov cr0, eax                 ; Set control register 0 to the A-register.


	jmp 0x8:LongMode

[BITS 64]
LongMode:
	cli
	mov rax, DataSel
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov rsp, 0x90000

	extern archMain
	call archMain

l0:	hlt
	jmp l0


global _moveToUserMode
_moveToUserMode:
push rax
	cli
	mov rax, 0x23
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	mov rax, rsp
	push 0x23
	push rax
	pushf
	pop rax
	or rax, 0x200
	push rax
	push 0x1b
	push .Nxt

	iretq
	.Nxt:
pop rax
ret

global _moveToUserModeNoInterrupts
_moveToUserModeNoInterrupts:
push rax
	cli
	mov rax, 0x23
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	mov rax, rsp
	push 0x23
	push rax
	pushf
	push 0x1b
	push .Nxt

	iretq
	.Nxt:
pop rax
ret


global _intHandler
_intHandler:
	extern interruptHandler
	call interruptHandler
iretq


extern gdt_ptr

global _gdtFlush
_gdtFlush:
push rax
	lgdt [gdt_ptr]

	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax

	mov rax, rsp
	push 0x10
	push rax
	pushf
	push 0x8
	push .Nxt

	iretq
	.Nxt:

pop rax
	ret

global _tssFlush
_tssFlush:
   mov ax, 0x2b
   ltr ax
   ret


global _doSysCall
_doSysCall:
	int 0x80
ret


[section .data]

global _kernelEnd
_kernelEnd dq _end

global _kernelStart
_kernelStart dq _code

global _kernelBss
_kernelBss dq _bss

;Basic GDT. Later on we set up a better one
global GDT64
GDT64:                           ; Global Descriptor Table (64-bit).
	.Null: equ $ - GDT64         ; The null descriptor.
	dw 0                         ; Limit (low).
	dw 0                         ; Base (low).
	db 0                         ; Base (middle)
	db 0                         ; Access.
	db 0                         ; Granularity.
	db 0                         ; Base (high).
	CodeSel equ $ - GDT64         ; The code descriptor.

	dw 0                         ; Limit (low).
	dw 0                         ; Base (low).
	db 0                         ; Base (middle)
	db 10011000b                 ; Access.
	db 00100000b                 ; Granularity.
	db 0                         ; Base (high).
	DataSel equ $ - GDT64         ; The data descriptor.

	dw 0                         ; Limit (low).
	dw 0                         ; Base (low).
	db 0                         ; Base (middle)
	db 10010010b                 ; Access.
	db 00100000b                 ; Granularity.
	db 0                         ; Base (high).

	.Pointer:                    ; The GDT-pointer.
	dw $ - GDT64 - 1             ; Limit.
	dq GDT64                     ; Base.

longModeError db 'L', 7, 'o', 7, 'n', 7, 'g', 7, ' ', 7, 'm', 7, 'o', 7, 'd', 7, 'e', 7, ' ', 7, 'u', 7, 'n', 7, 'a', 7, 'v', 7, 'a', 7, 'i', 7, 'l', 7, 'a', 7, 'b', 7, 'l', 7, 'e', 7


[section .bss]

;Basic page structures

global _pml4
_pml4 resq 512

global _pdpt
_pdpt resq 512

global _pd
_pd resq 512

global _pt
_pt resq 512
